import {
    App,
    ButtonGroupControl,
    DOM,
    GeoBounds,
    MousePositionControl,
    PolylineItemData,
    PolylinesEntity,
    fromWorld,
    toWorld
} from "vjmap3d";
import { env } from "./env";
import { GeoPoint } from "vjmap";
import { BufferAttribute, BufferGeometry, DoubleSide, Mesh, MeshStandardMaterial } from "three";
import { Lut } from "three/examples/jsm/math/Lut.js";

const createMesh = (dataMinZ: number, dataMaxZ: number, points: [number, number, number][], indices: number[]) => {
    let worldCoords: number[] = [];
    let lut = new Lut();
    
    let moveZ = 0
    if (env.baseZ !== 0) { // 如果为零时，按之前的高度绘制，否则都移至水平线上面
        moveZ = env.baseZ - dataMinZ;
    }
    lut.setMax(dataMaxZ);
    lut.setMin(dataMinZ);
    let colorArrays = [];
    //let worldPosArray = [];
    for (let k = 0; k < points.length; k++) {
        let cadPos: any = [];
        cadPos.push(points[k][0]);
        cadPos.push(points[k][1]);
        let z = points[k][2] + moveZ;
        colorArrays.push(...lut.getColor(points[k][2]).toArray());
        cadPos.push(z * env.scaleZ);

        let worldPos = toWorld(cadPos);
        worldCoords.push(...worldPos);
        //worldPosArray.push(worldPos)
    }

    let geometry = new BufferGeometry();
    
    geometry.setAttribute("position", new BufferAttribute(new Float32Array(worldCoords), 3));
    geometry.setIndex(new BufferAttribute(new Uint32Array(indices), 1));
    geometry.setAttribute("color", new BufferAttribute(new Float32Array(colorArrays), 3));
    // 将 UV 坐标设置到几何体 如果要纹理贴图，需要给定uv坐标，uv坐标得根据实际情况计算，下面的可供参考
    // let uvs = new Float32Array(worldPosArray.length * 2);
    // let xmin = Math.min(...worldPosArray.map(p => p.x));
    // let xmax = Math.max(...worldPosArray.map(p => p.x))
    // let ymin = Math.min(...worldPosArray.map(p => p.y))
    // let ymax = Math.max(...worldPosArray.map(p => p.y))
    // let zmin = Math.min(...worldPosArray.map(p => p.z))
    // let zmax = Math.max(...worldPosArray.map(p => p.z))
    // for(let c = 0; c < worldPosArray.length; c++) {
    //     // 计算 UV 坐标
    //     const u = (worldPosArray[c].x - xmin) / (xmax - xmin);
    //     const v = app.isMapMode ?
    //     (worldPosArray[c].y - ymin) / (ymax - ymin): 
    //     (worldPosArray[c].z - zmin) / (zmax - zmin);

    //     // 设置 UV 坐标
    //     uvs[c * 2] = u;
    //     uvs[c * 2 + 1] = v;
    // }
    // geometry.setAttribute('uv', new BufferAttribute(uvs, 2));
    geometry.computeVertexNormals();
    //   console.log(coords,triangles )
    let mat = new MeshStandardMaterial({
        //map: ResManager.loadTextureLinear("./uv_grid_opengl.jpg"), // 如果有uv坐标要进行纹理贴图
        vertexColors: true,
        // wireframe: true,
        side: DoubleSide
    });
    let mesh = new Mesh(geometry, mat);
    return {
        mesh,
        moveZ
    }
}
const drawPolyMesh = async (app: App) => {
    // 查找图中所有的多面网格实体坐标
    let query = await app.svc.conditionQueryFeature({
        // 支持的实体类型（列出一些支持的三维实体类型） https://vjmap.com/guide/svrStyleVar.html#%E6%94%AF%E6%8C%81%E7%9A%84cad%E5%AE%9E%E4%BD%93%E7%B1%BB%E5%9E%8B
        condition: `name='31' or name='32' or name='33' or name='34'`,
        fields: "objectid,points,indices",
        limit: 100000
    });
    let results = query.result;
    if (!results) return;
    let points = results.map((e: any) => {
        let pts = e.points.split(";");
        return pts.map((p: any) => {
            let pt = GeoPoint.fromString(p);
            pt.z ??= 0;
            return pt;
        }) as GeoPoint[];
    });
    let indices = results.map((e: any) => {
        return e.indices
            .replaceAll(";", ",")
            .split(",")
            .map((t: string) => +t);
    });

    let meshs = [];
    // 求出所有数据的最大最小值
    let dataMaxZArr: number[] = [], dataMinZArr: number[] = [];
    for (let n = 0; n < points.length; n++) {
        let maxZ = Math.max(...points[n].map((k: GeoPoint) => k.z));
        let minZ = Math.min(...points[n].map((k: GeoPoint) => k.z));
        dataMaxZArr.push(maxZ);
        dataMinZArr.push(minZ);
    }
    let dataMaxZ = Math.max(...dataMaxZArr)
    let dataMinZ = Math.min(...dataMinZArr)

    for (let n = 0; n < points.length; n++) {
        let ret = createMesh(dataMinZ, dataMaxZ, points[n].map((p: any) => [p.x, p.y, p.z]), indices[n])
        app.scene.add(ret.mesh);
        meshs.push({
            mesh: ret.mesh,
            moveZ: ret.moveZ
        });
    }
    return meshs;
};

const drawTerrain = async (app: App) => {
    // 查找图中所有的3dpolylines坐标
    let query = await app.svc.conditionQueryFeature({
        condition: `name='4'`,
        fields: "objectid,points",
        limit: 100000
    });
    let results = query.result;
    let polylines: PolylinesEntity;
    let addMeshDatas = []
    if (results && results.length > 0) {
        let polyData: PolylineItemData[] = [];

        let bounds = new GeoBounds();
        const x: number[] = [],
            y: number[] = [],
            z: number[] = [],
            t: number[] = [];

        let linePoints = results.map((e: any) => {
            let pts = e.points.split(";");
            return pts.map((p: any) => GeoPoint.fromString(p)) as GeoPoint[];
        });

    
        
        let minZ: number;
        let maxZ: number;
        linePoints.forEach((points: GeoPoint[]) => {
            for (let p of points) {
                if (!p.z) continue;
                x.push(p.x);
                y.push(p.y);
                z.push(0);
                t.push(p.z ?? 0);
                // @ts-ignore
                bounds.update([p]);

                if (p.z) {
                    if (!minZ) minZ = p.z;
                    if (p.z < minZ) minZ = p.z;
                    if (maxZ === undefined) maxZ = p.z;
                    if (p.z > maxZ) maxZ = p.z;
                }
            }
        });

        let isDrawPolyline3d = env.show3dpolyline
        if (isDrawPolyline3d) {
            let moveZ = 0;
            // @ts-ignore
            let dMinZ= minZ ?? 0, dMaxZ = maxZ ?? 0;
            if (env.baseZ !== 0) { // 如果为零时，按之前的高度绘制，否则都移至水平线上面
                moveZ = env.baseZ - dMinZ;
            }
            
            let lut = new Lut();
            // @ts-ignore
            lut.setMax(dMaxZ);
            // @ts-ignore
            lut.setMin(dMinZ);

            linePoints.forEach((points: GeoPoint[]) => {
                let coordinates: any = [];
                let color: any = [];
                for (let p of points) {
                    let z = (p.z ?? 0) + moveZ;
                    coordinates.push(toWorld([p.x, p.y, z * env.scaleZ]).toArray());
                    color.push(lut.getColor((p.z ?? 0)));
                }
                polyData.push({
                    color: color,
                    lineWidth: 2,
                    coordinates: coordinates
                });
            });

            polylines = new PolylinesEntity({
                //PolygonsEntity
                data: polyData,
                showVertex: false,
                // 不要被下面的面挡住
                style: {
                    polygonOffset: true,
                    polygonOffsetFactor: -6.0,
                    polygonOffsetUnits: 1
                }
            });
            polylines.addTo(app);
        }

        // @ts-ignore
        let dataMaxZ = maxZ ?? 0;
        // @ts-ignore
        let dataMinZ = minZ ?? 0;
        if (env.show3dpolyface)
        {
            let vertices: number[] = [];
            let coordZMap: any = {};
            linePoints.forEach((points: GeoPoint[]) => {
                for (let p of points) {
                    vertices.push(p.x, p.y);
                    const key = `${p.x.toFixed(6)}_${p.y.toFixed(6)}`;
                    coordZMap[key] = p.z ?? 0;
                }
            });

            let res = await app.svc.execCommand(
                "triangulate",
                {
                    vertices,
                    useCache: true
                },
                "_null",
                "v1",
                false
            );

            let points: [number, number, number][] = []
            for (let n = 0; n < res.vertices.length; n += 2) {
                let pt: [number, number, number] = [res.vertices[n], res.vertices[n + 1], 0]
                const key = `${res.vertices[n].toFixed(6)}_${res.vertices[n + 1].toFixed(6)}`;
                let z = 0;
                if (key in coordZMap) {
                    z = coordZMap[key];
                }
                pt[2] = z;
                points.push(pt)
            }
            let ret = createMesh(dataMinZ, dataMaxZ, points, res.triangles)
            let mesh = ret.mesh;
            app.scene.add(mesh);
            addMeshDatas.push({
                mesh: mesh,
                moveZ: ret.moveZ
            });
        }
    }
    if (env.show3dpolymesh) {
        let polyMeshs = await drawPolyMesh(app);
        addMeshDatas.push(...polyMeshs ?? [])
    }
  
    let mousePositionControl: MousePositionControl;
    const showMousePositionControl = () => {
        if (mousePositionControl) return;
        mousePositionControl = new MousePositionControl({
            UnProjectModes: app.isMapMode ? [true] : [true],
            labelFormat: (x, y, z, _, retIntersectObject) => {
                let cad = fromWorld([x, y, z]);
                let index = addMeshDatas.findIndex(m => retIntersectObject?.object?.object == m.mesh);
                if (index >= 0) {
                    // 如果是在mesh上
                    z = cad[2] / env.scaleZ  - addMeshDatas[index].moveZ;
                    return `${cad[0].toFixed(4)}, ${cad[1].toFixed(4)}, ${z.toFixed(4)}`;
                } else {
                    return `${cad[0].toFixed(4)}, ${cad[1].toFixed(4)}`;
                }
            }
        });
        app.addControl(mousePositionControl);
    };
    const hideMousePositionControl = () => {
        if (!mousePositionControl) return;
        app.removeControl(mousePositionControl);
        // @ts-ignore
        mousePositionControl = null;
    };
    if (env.showMousePositionControl) {
        showMousePositionControl();
    }

      
    // @ts-ignore
    if (!polylines && addMeshDatas.length == 0) {
        app.logInfo("没有相到图中的三维多段线或多面网格实体或相关的三维实体，无法自动生成三维模型", "warn", 10000)
    }

    return {
        // @ts-ignore
        polylines,
        meshs: addMeshDatas.map(m => m.mesh),
        showMousePositionControl,
        hideMousePositionControl
    };
};

export const initButtonGroup = async (app: App) => {
    let draw = await drawTerrain(app);
    let isHide = false;
    let control = new ButtonGroupControl({
        buttons: [
            {
                id: "hideline",
                html: "三维线隐藏/显示",
                title: "隐藏三维线",
                style: {
                    width: "130px"
                },
                onclick: async () => {
                    // @ts-ignore
                    if (draw.polylines) {
                        // @ts-ignore
                        draw.polylines.visible = !draw.polylines.visible;
                    }
                }
            },
            {
                id: "hidepoly",
                html: "三维网格隐藏/显示",
                title: "三维网格隐藏/显示",
                style: {
                    width: "130px"
                },
                onclick: async () => {
                    draw?.meshs.forEach(mesh => mesh.visible = !mesh.visible)
                }
            },
            {
                id: "hidemap",
                html: "底图隐藏/显示",
                title: "底图隐藏/显示",
                style: {
                    width: "130px"
                },
                onclick: async () => {
                    if (app.isMapMode) {
                        if (isHide) {
                            app.map.showSource("raster-source");
                        } else {
                            app.map.hideSource("raster-source");
                        }
                        isHide = !isHide;
                    } else {
                        app.mapViewEntity.visible = !app.mapViewEntity.visible;
                    }
                }
            },
            {
                id: "wireframe",
                html: "线宽模式开/关",
                title: "线宽模式开/关",
                style: {
                    width: "130px"
                },
                onclick: async () => {
                    draw?.meshs.forEach(mesh => mesh.material.wireframe = !mesh.material.wireframe)
                }
            },
            {
                id: "mouseposctrl",
                html: "鼠标位置/显示",
                title: "鼠标位置/显示",
                style: {
                    width: "130px"
                },
                onclick: async () => {
                    if (env.showMousePositionControl) {
                        draw?.hideMousePositionControl();
                    } else {
                        draw?.showMousePositionControl();
                    }
                    env.showMousePositionControl = !env.showMousePositionControl;
                }
            },
            {
                id: "switchmode",
                html: !app.isMapMode ? "2D图" : "3D图",
                title: !app.isMapMode ? "以2D图3D做为图层的形式打开查看" : "以3D形式打开查看",
                round: true,
                style: { width: "40px" },
                onclick: async () => {
                    let href = window.location.href;
                    if (href.indexOf("?") == -1) href += "?";
                    href = href.replace("&map2d=true", "");
                    href = href.replace("&map2d=false", "");
                    href = href.replace("map2d=true", "");
                    href = href.replace("map2d=false", "");
                    if (app.isMapMode) {
                        href += "&map2d=false";
                    } else {
                        href += "&map2d=true";
                    }
                    window.open(href);
                }
            }
        ]
    });
    app.addControl(control, "top-right");
};

export const showMessageTip = (app: App, message?: string) => {
    if (!message) return;
    let div = document.createElement("div");
    div.innerHTML = `<h4 style = "position:absolute;color:${
        env.darkTheme ? "yellow" : "blue"
    };top:30px;left:100px">${message}</h4>`;
    app.container.appendChild(div);
    setTimeout(() => DOM.remove(div), 5000);
};
